/*
 * This file is part of quickReminder, a software for gamers
 *
 * Copyright (C) 2006-2008 Lucas Romero
 *
 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with this program; if not, see <http://www.gnu.org/licenses/>.
 */
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Runtime.InteropServices;
//using MrSmarty.CodeProject;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Collections;
using LucasCode.Win32;

namespace LucasCode
{
    [Flags]
    public enum NotifyMethods
    {
        KeyboardNormal,
        KeyboardCompatibility,
        BeepHardware,
        BeepSoftware,
        OSD,
        OSDWinAPI,
        OSDOpenGL
    }
    public enum LEDs : int
    {
        CAPSLOCK = 0,
        NUMLOCK = 1,
        SCROLLLOCK = 2
    }
    public class WarnTimer
    {
        private Timer timer;
        private string m_text;
        public string Text
        {
            get
            {
                return m_text;
            }
        }
        public WarnTimer(string text, TimerCallback Callback, int timeout)
        {
            timer = new Timer(Callback, text, timeout, Timeout.Infinite);
            m_text = text;
        }
        public void Cancel()
        {
            timer.Change(0, Timeout.Infinite);
        }
    };
    public class TimerCollection : CollectionBase
    {
        public int Add(WarnTimer item)
        {
            return List.Add(item);
        }
        public void Insert(int index, WarnTimer item)
        {
            foreach(WarnTimer w in this)
            {
                if (item.Text == w.Text)
                {
                    w.Cancel();
                }
            }
            List.Insert(index, item);
        }
        public void Remove(WarnTimer item)
        {
            List.Remove(item);
        }
        public bool Contains(WarnTimer item)
        {
            return List.Contains(item);
        }
        public int IndexOf(WarnTimer item)
        {
            return List.IndexOf(item);
        }
        public void CopyTo(WarnTimer[] array, int index)
        {
            List.CopyTo(array, index);
        }
        public WarnTimer this[int index]
        {
            get { return (WarnTimer)List[index]; }
            set { List[index] = value; }
        }
    } 
    public class UserNotifier
    {
        #region Win32 API Calls
        [DllImport("user32.dll")]
        static extern IntPtr keybd_event(IntPtr KEY, IntPtr bScan, IntPtr dwFlags, IntPtr dwExtraInfo);
        [DllImport("user32.dll")]
        static extern IntPtr GetKeyState(IntPtr KEY);

        [DllImport("NTKbdLites.dll")]
        static extern IntPtr OpenKeyboardDevice(out IntPtr error);
        [DllImport("NTKbdLites.dll")]
        static extern IntPtr CloseKeyboardDevice(IntPtr KbdHandle);
        [DllImport("NTKbdLites.dll")]
        static extern IntPtr FlashKeyboardLight(IntPtr KbdHandle, uint LEDFLAG, int Duration);

        [DllImport("kernel32.dll")]
        public static extern bool Beep(int frequency, int duration);
        #endregion

        #region Win32 API Constants
        /*const UInt32 VK_CAPITAL = 0x14;
        const UInt32 VK_SCROLL = 0x91;
        const UInt32 VK_NUMLOCK = 0x90;*/
        const UInt32 KEYUP = 0x2;
        UInt32[] KeyConstantsNormal = { 4, 2, 1 };
        UInt32[] KeyConstantsCompat = { 0x14, 0x90, 0x91 };

        public const UInt32 KEYBOARD_CAPS_LOCK_ON = 4;
        public const UInt32 KEYBOARD_NUM_LOCK_ON = 2;
        public const UInt32 KEYBOARD_SCROLL_LOCK_ON = 1;
        #endregion


        Thread NotifyThread;
        //private FloatingOSDWindow osd;
        public NotifyMethods NotifyMethod = NotifyMethods.OSDWinAPI;
        string WarnMessage = "ERROR!\nBLABLABLABLABLAB\nARJHDKJHD";
        StringCollection WarnMessages = new StringCollection();
        TimerCollection timers = new TimerCollection();

        System.Resources.ResourceManager res;
        Image WarnImage;


        public UserNotifier()
        {
            res = new System.Resources.ResourceManager("UserNotifier.images", this.GetType().Assembly);
            WarnImage = (Image)res.GetObject("messagebox_warning");
        }
        ~UserNotifier()
        {
            StopNotify();
        }
        private void Notify()
        {
            switch (NotifyMethod)
            {
                case NotifyMethods.KeyboardCompatibility:
                    IntPtr Key = new IntPtr(KeyConstantsCompat[BlinkLED]);
                    //int scroll0 = (int)GetKeyState(Key);
                    while (!aborted)
                    {
                        keybd_event(Key, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
                        Thread.Sleep(BlinkDuration);
                        keybd_event(Key, IntPtr.Zero, new IntPtr(KEYUP), IntPtr.Zero);
                        if (BlinkInterval >= BlinkDuration)
                            Thread.Sleep(BlinkInterval - BlinkDuration);

                    }
                    //keybd_event(new IntPtr(VK_SCROLL), IntPtr.Zero, new IntPtr(KEYUP), IntPtr.Zero);
                    /*if (scroll0 != (int)GetKeyState(Key))
                    {
                        keybd_event(Key, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
                        Thread.Sleep(1);
                        keybd_event(Key, IntPtr.Zero, new IntPtr(KEYUP), IntPtr.Zero);
                    }*/
                    break;
                case NotifyMethods.KeyboardNormal:
                    IntPtr KbdHandle = IntPtr.Zero, ErrNum = IntPtr.Zero;
                    try
                    {
                        KbdHandle = OpenKeyboardDevice(out ErrNum);
                        while (!aborted)
                        {

                            FlashKeyboardLight(KbdHandle, KeyConstantsNormal[BlinkLED], BlinkDuration);
                            if (BlinkInterval >= BlinkDuration)
                                Thread.Sleep(BlinkInterval - BlinkDuration);
                        }
                    }
                    catch
                    {
                        StopNotify();
                    }
                    finally
                    {
                        if (KbdHandle != IntPtr.Zero)
                            CloseKeyboardDevice(KbdHandle);
                    }
                    break;
                case NotifyMethods.BeepHardware:
                    while (!aborted)
                    {
                        Beep(BeepFrequency, BeepDuration);
                        if (BeepInterval >= BeepDuration)
                            Thread.Sleep(BeepInterval - BeepDuration);
                    }
                    break;
                case NotifyMethods.OSD:
                    //osd = new FloatingOSDWindow();
                    //osd.Show(new Point(0, 0), 255, Color.Red, OSDFont, OSDDuration, 0, 0, "No Demo recording!");
                    //Thread.Sleep(Timeout.Infinite);
                    /*while (!aborted)
                    {
                        osd.Show(new Point(0, 0), 255, Color.Red, OSDFont,OSDDuration, 0, 0, "No Demo recording!");
                        Thread.Sleep(OSDDuration);
                        //osd.Hide();
                        if (OSDInterval >= OSDDuration)
                            Thread.Sleep(OSDInterval - OSDDuration);
                    }*/
                    break;
                case NotifyMethods.OSDWinAPI:
                    
                    Thread.CurrentThread.Priority = ThreadPriority.Highest;
                    //IntPtr cs = User32.GetDC(User32.GetForegroundWindow());
                    //WarnPath.FillMode = FillMode.Winding;
                    Graphics g = null;// = Graphics.FromHdc(cs);
                    

                    while (!aborted/* && GameProcess.MainWindowHandle != IntPtr.Zero*/)
                    {
                        /*if (WindowHandle == IntPtr.Zero && GameProcess != null)
                        {
                            GameProcess.Refresh();
                            if (GameProcess.MainWindowHandle != IntPtr.Zero)
                            {
                                g.Dispose();
                                g = Graphics.FromHdc(User32.GetDC(WindowHandle = GameProcess.MainWindowHandle));
                                g.SmoothingMode = SmoothingMode.None;
                                g.InterpolationMode = InterpolationMode.Low;
                            }
                        }*/
                        
                        /*if (WindowHandle != GameProcess.MainWindowHandle && GameProcess != null)
                        {
                            if (GameProcess.MainWindowHandle != IntPtr.Zero)
                            {
                                g.Dispose();
                                g = Graphics.FromHdc(User32.GetDC(WindowHandle = GameProcess.MainWindowHandle));
                                g.SmoothingMode = SmoothingMode.None;
                                g.InterpolationMode = InterpolationMode.Low;
                            }
                        }*/
                        //try
                        //{

                        if (GameProcess != null)
                        {
                            //try
                            //{
                            if (GameProcess.MainWindowHandle == IntPtr.Zero)
                            {
                                GameProcess.Refresh();
                            }
                            if (g == null || GameProcess.MainWindowHandle == IntPtr.Zero)
                                g = Graphics.FromHwnd(GameProcess.MainWindowHandle);
                            if (!GameProcess.HasExited)
                            {
                                //if (g != null)
                                    //g.Dispose();
                                
                                if (g != null)
                                {
                                    //g.SmoothingMode = SmoothingMode.None;
                                    //g.InterpolationMode = InterpolationMode.Low;
                                    if (DrawWarnImage)
                                        g.DrawImageUnscaled(WarnImage, 0, 0);
                                    Monitor.Enter(WarnPath);
                                    g.FillPath(new SolidBrush(OSDColor), WarnPath);
                                    Monitor.Exit(WarnPath);
                                }
                            }
                            //}
                            //catch { }
                            //finally {  }
                            //g.DrawString(WarnMessage, OSDFont, new SolidBrush(OSDColor), new PointF(0, 0));
                        }
                        Thread.Sleep(OSDRepaintInterval);
                        
                    }
                    if (g != null)
                        g.Dispose();
                    WindowHandle = IntPtr.Zero;
                    break;
                case NotifyMethods.OSDOpenGL:
                                        break;
            }
        }
        struct CWPRETSTRUCT {
            public IntPtr lResult;
            public IntPtr lParam;
            public IntPtr wParam;
            public uint message;
            public IntPtr hwnd;
        };

        private int HookerCallback(int zahl, IntPtr a, IntPtr b, ref bool janein)
        {
            CWPRETSTRUCT info = new CWPRETSTRUCT();
            Marshal.PtrToStructure(a, info);
            Trace.WriteLine(info.message.ToString());
            return 0;
        }

        public void StartNotify()
        {
            if (NotifyThread == null || NotifyThread.ThreadState == System.Threading.ThreadState.Aborted || NotifyThread.ThreadState == System.Threading.ThreadState.Stopped)
            {
                aborted = false;
                NotifyThread = new Thread(new ThreadStart(Notify));
                NotifyThread.Start();
            }
            /*else
            {
                StopNotify();
                StartNotify();
            }*/
        }
        public void StopNotify()
        {
            WarnMessages.Clear();
            while (NotifyThread != null && (NotifyThread.ThreadState != System.Threading.ThreadState.Stopped))
            {
                aborted = true;
                Thread.Sleep(50);
            }
        }
        public void AddWarning(string Warning)
        {
            if(!WarnMessages.Contains(Warning))
            {
                WarnMessages.Add(Warning);
                UpdateWarnString();
            }
        }
        private void RemoveWarning(object Warning)
        {
            RemoveWarning((string)Warning);
        }
        public void AddWarning(string Warning, int timeout)
        {
            if (IgnoreWarnings)
                return;
            //Timer t = new Timer(new TimerCallback(RemoveWarning), Warning, timeout, Timeout.Infinite);
            AddWarning(Warning);
            timers.Add(new WarnTimer(Warning, new TimerCallback(RemoveWarning), timeout));
        }
        public void RemoveWarning(string Warning)
        {
            if (WarnMessages.Contains(Warning))
            {
                WarnMessages.Remove(Warning);
                UpdateWarnString();
            }
        }
        public void UpdateWarnString()
        {
            if (WarnMessages.Count > 0)
            {
                StringBuilder b = new StringBuilder();
                foreach (String s in WarnMessages)
                {
                    b.Append(s);
                    b.Append('\n');
                }
                WarnMessage = b.ToString();
                Monitor.Enter(WarnPath);
                WarnPath.Reset();
                WarnPath.AddString(WarnMessage, OSDFont.FontFamily, (int)OSDFont.Style, OSDFont.Size, new Point(0, 0), new StringFormat());
                Matrix m = new Matrix();
                m.Translate(50, 0);
                WarnPath.Transform(m);
                Monitor.Exit(WarnPath);
                StartNotify();
            }
            else
            {
                WarnMessage = String.Empty;
                StopNotify();
            }
            
        }
        /*public void ShowOSD()
        {
            osd.Show(new Point(0, 0), 255, Color.Red, OSDFont, 99999, 0, 0, "No Demo recording!");
        }
        public void HideOSD()
        {
            osd.Hide();
        }*/

        public bool DrawWarnImage = true;
        public bool aborted = false;
        public bool IgnoreWarnings = false;
        public int BlinkLED = (int)LEDs.SCROLLLOCK;
        public int BlinkDuration = 250;
        public int BlinkInterval = 2000;
        public int BeepFrequency = 1000;
        public int BeepInterval = 2000;
        public int BeepDuration = 500;
        public IntPtr WindowHandle = IntPtr.Zero;
        public int OSDDuration = 1000;
        public int OSDInterval = 3000;
        public int OSDRepaintInterval = 10;
        public Font OSDFont = new Font("Tahoma", 30);
        GraphicsPath WarnPath = new GraphicsPath();
        public Process GameProcess;
        public Color OSDColor = Color.Red;
        }
        //private int realblinkdur;
}
